package com.zkt.auth.domain.service.impl;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zkt.auth.common.enums.AuthStatusEnum;
import com.zkt.auth.common.constants.AuthConstant;
import com.zkt.auth.common.util.LoginUtil;
import com.zkt.auth.domain.convert.AuthUserConverter;
import com.zkt.auth.domain.entity.AuthUserBO;
import com.zkt.auth.domain.redis.RedisUtil;
import com.zkt.auth.domain.service.AuthUserDomainService;
import com.zkt.auth.infra.basic.entity.*;
import com.zkt.auth.infra.basic.service.*;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.LinkedList;
import java.util.List;

/**
 * @author 赵开泰
 * @program jc-club
 * @date 2024/9/30
 * @description 用户领域服务
 **/

@Service
@Slf4j
public class AuthUserDomainServiceImpl implements AuthUserDomainService {
	
	@Resource
	private IAuthUserService authUserService;
	
	@Resource
	private IAuthRoleService authRoleService;
	
	@Resource
	private IAuthUserRoleService authUserRoleService;
	
	@Resource
	private IAuthPermissionService authPermissionService;
	
	@Resource
	private IAuthRolePermissionService authRolePermissionService;
	
	@Resource
	private RedisUtil redisUtil;
	
	@Resource
	private ObjectMapper objectMapper;
	
	/**
	 * 用户登录
	 *
	 * @param authUserBO 用户登录信息
	 * @return 是否成功
	 */
	@Override
	public SaTokenInfo login(AuthUserBO authUserBO) throws Exception {
		AuthUser authUser = AuthUserConverter.INSTANCE.convertBoToAuthUser(authUserBO);
		authUser.setPassword(SaSecureUtil.sha1(authUser.getPassword()));
		AuthUser user = authUserService.lambdaQuery()
				.eq(AuthUser::getUserName, authUser.getUserName())
				.one();
		
		if (user == null) {
			throw new Exception("用户不存在");
		}
		if (!user.getPassword().equals(authUser.getPassword())) {
			throw new Exception("用户名或密码错误");
		}
		
		StpUtil.login(user.getUserName());
		
		return StpUtil.getTokenInfo();
	}
	
	/**
	 * 用户验证码登录
	 *
	 * @param captcha 验证码
	 * @return 鉴权信息
	 */
	@Override
	public SaTokenInfo loginByCaptcha(String captcha) {
		String captchaKey = redisUtil.buildKey(AuthConstant.CAPTCHA_PREFIX, captcha);
		String openId = redisUtil.get(captchaKey);
		
		if (StrUtil.isBlank(openId)) {
			return null;
		}
		
		AuthUserBO authUserBO = new AuthUserBO();
		authUserBO.setUserName(openId);
		this.register(authUserBO);
		
		StpUtil.login(openId);
		
		return StpUtil.getTokenInfo();
	}
	
	/**
	 * 退出登录
	 *
	 * @return 是否成功
	 */
	@Override
	public Boolean logout() {
		StpUtil.logout();
		return true;
	}
	
	/**
	 * 注册用户
	 *
	 * @param authUserBO 用户注册信息
	 * @return 是否成功
	 */
	@SneakyThrows
	@Override
	@Transactional(rollbackFor = Exception.class)
	public Boolean register(AuthUserBO authUserBO) {
		AuthUser authUser = AuthUserConverter.INSTANCE.convertBoToAuthUser(authUserBO);
		
		// 校验用户是否存在
		boolean exists = authUserService.lambdaQuery()
				.eq(AuthUser::getUserName, authUser.getUserName())
				.exists();
		if (exists) {
			return true;
		}
		
		if (StrUtil.isNotBlank(authUser.getPassword())) {
			authUser.setPassword(SaSecureUtil.sha1(authUser.getPassword()));
		}
		if (authUser.getStatus() == null) {
			authUser.setStatus(AuthStatusEnum.OPEN.getCode());
		}
		
		boolean userSave = authUserService.save(authUser);
		
		// 建立一个初步的角色关联
		AuthRole authRole = authRoleService.lambdaQuery()
				.eq(AuthRole::getRoleKey, AuthConstant.NORMAL_USER)
				.one();
		
		AuthUserRole authUserRole = new AuthUserRole();
		authUserRole.setUserId(authUser.getId());
		authUserRole.setRoleId(authRole.getId());
		
		boolean userRoleSave = authUserRoleService.save(authUserRole);
		
		// 要把当前用户的角色和权限都刷到redis里
		String roleKey = redisUtil.buildKey(AuthConstant.AUTH_ROLE_PREFIX, authUser.getUserName());
		List<AuthRole> authRoleList = new LinkedList<>();
		
		authRoleList.add(authRole);
		
		redisUtil.set(roleKey, objectMapper.writeValueAsString(authRoleList));
		
		List<AuthRolePermission> authRolePermissionList = authRolePermissionService.lambdaQuery()
				.eq(AuthRolePermission::getRoleId, authRole.getId())
				.list();
		
		List<Long> permissionIdList = authRolePermissionList.stream().map(AuthRolePermission::getPermissionId).toList();
		// 根据permissionId查权限
		List<AuthPermission> authPermissionList = authPermissionService.listByIds(permissionIdList);
		
		String permissionKey = redisUtil.buildKey(AuthConstant.AUTH_PERMISSION_PREFIX, authUser.getUserName());
		redisUtil.set(permissionKey, objectMapper.writeValueAsString(authPermissionList));
		
		return userSave && userRoleSave;
	}
	
	/**
	 * 更新用户信息
	 *
	 * @param authUserBO 更新后的用户信息
	 * @return 是否成功
	 */
	@Override
	public Boolean update(AuthUserBO authUserBO) {
		AuthUser authUser = AuthUserConverter.INSTANCE.convertBoToAuthUser(authUserBO);
		if (ObjUtil.isNull(authUser.getId())) {
			authUser.setId(this.getUserInfo(LoginUtil.getLoginId()).getId());
		}
		
		if (StrUtil.isNotBlank(authUser.getPassword())) {
			authUser.setPassword(SaSecureUtil.sha1(authUser.getPassword()));
		}
		
		boolean update = authUserService.updateById(authUser);
		// 有任何的更新都要与缓存进行同步
		return update;
	}
	
	/**
	 * 删除用户
	 *
	 * @param authUserBO 需要删除的用户信息
	 * @return 是否成功
	 */
	@Override
	public Boolean delete(AuthUserBO authUserBO) {
		AuthUser authUser = AuthUserConverter.INSTANCE.convertBoToAuthUser(authUserBO);
		boolean remove = authUserService.removeById(authUser.getId());
		// 与缓存进行同步
		return remove;
	}
	
	/**
	 * 用户启用/禁用
	 *
	 * @param authUserBO 用户信息
	 * @return 是否成功
	 */
	@Override
	public Boolean changeStatus(AuthUserBO authUserBO) {
		Boolean update = this.update(authUserBO);
		// 与缓存进行同步
		return update;
	}
	
	/**
	 * 获取用户信息
	 *
	 * @return 用户信息
	 */
	@Override
	public AuthUserBO getUserInfo(String username) {
		if (StrUtil.isBlank(username)) {
			username = LoginUtil.getLoginId();
		}
		
		AuthUser userInfo = authUserService.lambdaQuery()
				.eq(AuthUser::getUserName, username)
				.one();
		
		AuthUserBO authUserBO = AuthUserConverter.INSTANCE.convertAuthUserToBo(userInfo);
		authUserBO.setPassword(null);
		
		return authUserBO;
	}
}